package de.playmee.handler;

import java.util.ArrayList;
import java.util.List;

import de.playmee.Channel;
import de.playmee.R;
import de.playmee.enums.Mood;
import de.playmee.enums.Release;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

public class ChannelHandler extends DatabaseHandler {

	private final String TAG = "ChannelHandler";

	public final static String KEY_ID = "_id";
	public final static String KEY_LABEL = "label";
	public final static String KEY_IMAGE = "image";
	public final static String KEY_DURATION = "duration";
	public final static String KEY_BPM = "bpm";
	public final static String KEY_SEASON = "season";
	public final static String KEY_WEATHER = "weather";
	public final static String KEY_TIMEOFDAY = "timeOfDay";
	
	public final static String KEY_MOODS = "moods";
	public final static String KEY_RELEASES = "releases";

	public final static String DATABASE_TABLE = "channel";

	public final static String DATABASE_CREATE = "create table " + DATABASE_TABLE
			+ " (" + KEY_ID + " integer primary key autoincrement, "
			+ KEY_LABEL + " text not null, "
			+ KEY_IMAGE + " integer not null, "
			+ KEY_DURATION + " integer not null, "
			+ KEY_BPM + " integer not null, "
			+ KEY_SEASON + " text not null, "
			+ KEY_WEATHER + " text not null, "
			+ KEY_TIMEOFDAY + " text not null, "
			+ KEY_MOODS + " text not null, "
			+ KEY_RELEASES + " text not null);";
	
	// order by label (a..z, ignore case)
	public final static String ORDER_BY_LABEL_ABC = KEY_LABEL + " collate nocase asc";
	
	// order by label (z..a, ignore case)
	public final static String ORDER_BY_LABEL_CBA = KEY_LABEL + " collate nocase desc";
	
	private ChannelTrackRatingHandler ctrHandler;

	public ChannelHandler(DatabaseHelper dbHelper, ChannelTrackRatingHandler ctrHandler) {
		super(dbHelper);
		this.ctrHandler = ctrHandler;
		// now available: dbHelper, db, DELIMITER
	}

	/**
	 * inserts a channel into the database
	 * 
	 * @param channel
	 * @return true if channel was inserted successfully, false otherwise
	 */
	public boolean insertChannel(Channel channel, boolean insertChannelTrackRating) {
		Log.i(TAG, "Inserting channel (" + channel.getLabel() + ")...");
		ContentValues initialValues = getContentValues(channel);
		synchronized(db) {
			long id = db.insert(DATABASE_TABLE, null, initialValues);
			if (id != -1) {
				channel.setId(id);
				if (insertChannelTrackRating)
					ctrHandler.insertChannelTrackRatings(channel, null);
				return true;
			}
		}
		return false;
	}

	/**
	 * deletes a particular channel
	 * 
	 * @param _id
	 * @return {@link SQLiteDatabase#delete(String, String, String[])}
	 */
	public boolean deleteChannel(long _id) {
		Log.i(TAG, "Deleting channel " + _id + "...");
		synchronized(db) {
			return db.delete(DATABASE_TABLE, KEY_ID + "=" + _id, null) > 0;
		}
	}
	
	/**
	 * retrieves all channels
	 * 
	 * @param selection
	 * @param groupBy
	 * @param having
	 * @param orderBy
	 * @return list
	 */
	public ArrayList<Channel> loadChannels(String selection, String groupBy, String having, String orderBy) {
		Log.i(TAG, "Querying all channels...");
		ArrayList<Channel> channels = new ArrayList<Channel>();
		synchronized(db) {
			Cursor cur = db.query(DATABASE_TABLE, null, selection, null, groupBy, having, orderBy);
			if (cur != null && cur.moveToFirst()) {
				do {
					Channel channel = createChannel(cur);
					channels.add(channel);
				} while (cur.moveToNext());
			}
		}
		return channels;
	}
	
	/**
	 * retrieves all channel ids
	 * 
	 * @param selection
	 * @param groupBy
	 * @param having
	 * @param orderBy
	 * @return
	 */
	public List<Long> loadChannelIds(String selection, String groupBy, String having, String orderBy) {
		Log.i(TAG, "Querying all channel ids...");
		List<Long> ids = new ArrayList<Long>();
		synchronized(db) {
			String[] columns = { KEY_ID };
			Cursor cur = db.query(DATABASE_TABLE, columns, selection, null, groupBy, having, orderBy);
			if (cur != null && cur.moveToFirst()) {
				int idIndex = cur.getColumnIndex(KEY_ID);
				do {
					ids.add(cur.getLong(idIndex));
				} while (cur.moveToNext());
			}
		}
		return ids;
	}
	
	/**
	 * retrieves all channel labels
	 * 
	 * @param selection
	 * @param groupBy
	 * @param having
	 * @param orderBy
	 * @return
	 */
	public List<String> loadChannelLabels(String selection, String groupBy, String having, String orderBy) {
		Log.i(TAG, "Querying all channel labels...");
		List<String> labels = new ArrayList<String>();
		synchronized(db) {
			String[] columns = { KEY_LABEL };
			Cursor cur = db.query(DATABASE_TABLE, columns, selection, null, groupBy, having, orderBy);
			if (cur != null && cur.moveToFirst()) {
				int labelIndex = cur.getColumnIndex(KEY_LABEL);
				do {
					labels.add(cur.getString(labelIndex).trim());
				} while (cur.moveToNext());
			}
		}
		return labels;
	}

	/**
	 * retrieves a particular channel
	 * 
	 * @param _id
	 * @return Channel
	 */
	public Channel loadChannel(long _id) {
		Log.i(TAG, "Queriying particular channel (" + _id + ")...");
		Channel channel = null;
		synchronized(db) {
			Cursor cur = db.query(true, DATABASE_TABLE, null, KEY_ID
					+ "=" + _id, null, null, null, null, null);
			if (cur != null && cur.moveToFirst()) {
				channel = createChannel(cur);
			}
		}
		return channel;
	}
	
	/**
	 * creates a channel from given cursor
	 * 
	 * @param cur
	 * @return Channel
	 */
	public Channel createChannel(Cursor cur) {
		// get column indices
		int idIndex = cur.getColumnIndex(KEY_ID);
		int labelIndex = cur.getColumnIndex(KEY_LABEL);
		int imageIndex = cur.getColumnIndex(KEY_IMAGE);
		int durationIndex = cur.getColumnIndex(KEY_DURATION);
		int bpmIndex = cur.getColumnIndex(KEY_BPM);
		int seasonIndex = cur.getColumnIndex(KEY_SEASON);
		int weatherIndex = cur.getColumnIndex(KEY_WEATHER);
		int timeOfDayIndex = cur.getColumnIndex(KEY_TIMEOFDAY);
		int moodsIndex = cur.getColumnIndex(KEY_MOODS);
		int releasesIndex = cur.getColumnIndex(KEY_RELEASES);
		
		// get values of the row
		long id = cur.getLong(idIndex);
		String label = cur.getString(labelIndex).trim();
		int image = cur.getInt(imageIndex);
		int duration = cur.getInt(durationIndex);
		int bpm = cur.getInt(bpmIndex);
		boolean considerSeason = Boolean.parseBoolean(cur.getString(seasonIndex).trim());
		boolean considerWeather = Boolean.parseBoolean(cur.getString(weatherIndex).trim());
		boolean considerTimeOfDay = Boolean.parseBoolean(cur.getString(timeOfDayIndex).trim());
		String moods = cur.getString(moodsIndex);
		String releases = cur.getString(releasesIndex);
		
		// create channel and define attributes
		Channel channel = new Channel(id, label);
		channel.setImage(image);
		channel.setDuration(duration);
		channel.setBpm(bpm);
		channel.considerSeason(considerSeason);
		channel.considerWeather(considerWeather);
		channel.considerTimeOfDay(considerTimeOfDay);
		if (!moods.isEmpty()) {
			String[] m = moods.split(DELIMITER);
			for (String mood : m) {
				channel.getMoods().add(Mood.getById(Integer.parseInt(mood.trim())));
			}
		}
		if (!releases.isEmpty()) {
			String[] r = releases.split(DELIMITER);
			for (String release : r) {
				channel.getReleases().add(Release.getById(Integer.parseInt(release.trim())));
			}
		}
		
		return channel;
	}

	/**
	 * updates a channel
	 * 
	 * @param channel
	 * @return {@link SQLiteDatabase#update(String, ContentValues, String, String[])}
	 */
	public boolean updateChannel(Channel channel) {
		Log.i(TAG, "Updating channel (" + channel.getId() + ")...");
		ContentValues args = getContentValues(channel);
		synchronized(db) {
			return db.update(DATABASE_TABLE, args, KEY_ID + "=" + channel.getId(), null) > 0;
		}
	}
	
	/**
	 * creates ContentValues with all attributes of the channel
	 * 
	 * @param channel
	 * @return ContentValues
	 */
	private ContentValues getContentValues(Channel channel) {
		ContentValues args = new ContentValues();
		args.put(KEY_LABEL, channel.getLabel());
		args.put(KEY_IMAGE, channel.getImage());
		args.put(KEY_DURATION, channel.getDuration());
		args.put(KEY_BPM, channel.getBpm());
		args.put(KEY_SEASON, channel.isConsideringSeason() ? "true" : "false");
		args.put(KEY_WEATHER, channel.isConsideringWeather() ? "true" : "false");
		args.put(KEY_TIMEOFDAY, channel.isConsideringTimeOfDay() ? "true" : "false");
		
		String moods = "";
		for (Mood mood : channel.getMoods()) {
			if (!moods.isEmpty())
				moods += ",";
			moods += mood.getId();
		}
		args.put(KEY_MOODS, moods);
		
		String releases = "";
		for (Release release : channel.getReleases()) {
			if (!releases.isEmpty())
				releases += ",";
			releases += release.getId();
		}
		args.put(KEY_RELEASES, releases);
		
		return args;
	}
	
	/**
	 * adds some default channels to the list of all channels
	 */
	private void insertDefaultChannels() {
		Log.i(TAG, "Inserting default channels...");
		Channel channel;

		// Home
		channel = new Channel("Home");
		channel.considerTimeOfDay(true);
		channel.considerSeason(true);
		channel.considerWeather(true);
		channel.setImage(R.drawable.house);
		// insert channel into database sets the id automatically
		insertChannel(channel, false);

		// Work
		channel = new Channel("Work");
		channel.considerTimeOfDay(true);
		channel.getMoods().add(Mood.Stressed);
		channel.setImage(R.drawable.office);
		insertChannel(channel, false);

		// Jogging
		channel = new Channel("Jogging");
		channel.getMoods().add(Mood.Energetic);
		channel.setBpm(120);
		channel.setImage(R.drawable.running);
		insertChannel(channel, false);
		
		// Chill
		channel = new Channel("Chill");
		channel.getMoods().add(Mood.Relaxed);
		channel.getMoods().add(Mood.Bored);
		channel.considerSeason(true);
		channel.considerWeather(true);
		channel.considerTimeOfDay(true);
		channel.setImage(R.drawable.chill);
		insertChannel(channel, false);
		
		// Season
		channel = new Channel("Season");
		channel.considerSeason(true);
		channel.considerWeather(true);
		channel.setImage(R.drawable.snowman);
		insertChannel(channel, false);
		
		// Party
		channel = new Channel("Party");
		channel.getMoods().add(Mood.Energetic);
		channel.getReleases().add(Release.LastYear);
		channel.getReleases().add(Release.ThisYear);
		channel.setImage(R.drawable.party);
		insertChannel(channel, false);
		
		// In love
		channel = new Channel("Romantic");
		channel.getMoods().add(Mood.InLove);
		channel.getMoods().add(Mood.Relaxed);
		channel.considerSeason(true);
		channel.setImage(R.drawable.romantic);
		insertChannel(channel, false);
		
		Log.i(TAG, "7 default channels created.");
	}
	
	/**
	 * prepares the channel handler
	 */
	public void prepare() {
		if (tableIsEmpty(DATABASE_TABLE)) {
			insertDefaultChannels();
		}
	}
}
